package com.gaff.emp.core.util;

import cn.hutool.core.io.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.core.io.Resource;

import javax.crypto.Cipher;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.StringReader;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;

@Slf4j
public class CertUtil {

    public static X509Certificate parseCertificate(String base64) throws Exception {
        byte[] bytes = Base64.decodeBase64(base64);
        return parseCertificate(bytes);
    }

    public static X509Certificate parseCertificate(byte[] bytes) throws Exception {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
        CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509");
        X509Certificate certificate = (X509Certificate) certificatefactory.generateCertificate(inputStream);
        return certificate;
    }

    public static X509Certificate parseCertificate(Resource resource) throws Exception {
        CertificateFactory certificatefactory = CertificateFactory.getInstance("X.509");
        X509Certificate certificate = (X509Certificate) certificatefactory.generateCertificate(resource.getInputStream());
        return certificate;
    }

    public static State checkCertificateState(File file) {
        try {
            byte[] bytes = FileUtil.readBytes(file);
            X509Certificate certificate = parseCertificate(bytes);
            certificate.checkValidity();
            return State.SUCCEED;
        } catch (CertificateExpiredException e) {
            return State.EXPIRED;
        } catch (CertificateNotYetValidException e) {
            return State.NOT_YET;
        } catch (Exception e) {
            return State.INVALID;
        }
    }

    public static State checkCertificateState(String base64) {
        try {
            X509Certificate certificate = parseCertificate(base64);
            certificate.checkValidity();
            return State.SUCCEED;
        } catch (CertificateExpiredException e) {
            return State.EXPIRED;
        } catch (CertificateNotYetValidException e) {
            return State.NOT_YET;
        } catch (Exception e) {
            return State.INVALID;
        }
    }

    public static PrivateKey getPrivateKey(String base64, String algorithm) throws Exception {
        byte[] bytes = Base64.decodeBase64(base64);
        return getPrivateKey(bytes, algorithm);
    }

    public static PrivateKey getPrivateKey(byte[] bytes, String algorithm) throws Exception {
        BufferedReader reader = new BufferedReader(new StringReader(new String(bytes)));
        String s = null;
        StringBuffer privateKeyString = new StringBuffer();
        while (true) {
            s = reader.readLine();
            if (s.contains("BEGIN ")) {
                continue;
            }
            if (s.contains("END ")) {
                break;
            }
            privateKeyString.append(s + "\r\n");
        }
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyString.toString()));
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    public static byte[] encrypt(PrivateKey privateKey, byte[] plainData) {
        try {
            Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            return cipher.doFinal(plainData);
        } catch (Exception e) {
            throw new RuntimeException(e.getLocalizedMessage());
        }
    }

    public static byte[] decrypt(PublicKey publicKey, byte[] cipherData) {
        try {
            Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            return cipher.doFinal(cipherData);
        } catch (Exception e) {
            throw new RuntimeException(e.getLocalizedMessage());
        }
    }

    public static boolean isMatch(String cert, String key) {
        try {
            X509Certificate certificate = parseCertificate(cert);
            PublicKey publicKey = certificate.getPublicKey();
            String algorithm = certificate.getPublicKey().getAlgorithm();
            PrivateKey privateKey = getPrivateKey(key, algorithm);

            String plainText = "test";
            byte[] cipher = encrypt(privateKey, plainText.getBytes());
            byte[] plainBytes = decrypt(publicKey, cipher);
            return plainText.equals(new String(plainBytes));
        } catch (Exception e) {
            log.error("isMatch", e);
            return false;
        }
    }

    public enum State {

        INVALID("证书无效"),
        EXPIRED("证书过期"),
        NOT_YET("证书未启用"),
        SUCCEED("证书有效");

        private String message;

        State(String message) {
            this.message = message;
        }

        public String getMessage() {
            return message;
        }
    }

}
